package cn.flying.cloud.task.core.quartz.job;

import javax.annotation.Resource;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang3.exception.ExceptionUtils;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.PersistJobDataAfterExecution;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.stereotype.Component;
import org.springframework.web.client.ResourceAccessException;
import org.springframework.web.client.RestTemplate;

import cn.flying.cloud.base.common.utils.Ft;
import cn.flying.cloud.base.common.utils.date.DateTimeUtil;
import cn.flying.cloud.base.common.vo.Rt;
import cn.flying.cloud.task.bo.TaskJobInfoBo;
import cn.flying.cloud.task.bo.TaskJobLogBo;
import cn.flying.cloud.task.core.quartz.constants.TaskConstant;
import cn.flying.cloud.task.core.quartz.enums.JobExecuteStatusEnum;
import cn.flying.cloud.task.core.quartz.loadbalance.RoundLoadBalance;
import cn.flying.cloud.task.service.TaskJobLogService;

/**
 * @author: admin
 * @date: 2025年03月12日 20:19
 * @version: 1.0
 */
@DisallowConcurrentExecution  // 默认情况下，同一个Job会并发执行 添加@DisallowConcurrentExecution注解，禁止并发进行
@PersistJobDataAfterExecution
@Component
public class ScheduleJobExecution extends QuartzJobBean {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Resource
    private RoundLoadBalance loadBalance;
    @Resource
    private RestTemplate restTemplate;
    @Resource
    private TaskJobLogService taskJobLogService;

    /**
     * @param context
     * @throws JobExecutionException
     */
    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        TaskJobInfoBo jobInfo = new TaskJobInfoBo();

        JobDataMap jobDataMap = context.getMergedJobDataMap();
        BeanUtils.copyProperties(jobDataMap.get(TaskConstant.JOB_PERSISTENCE), jobInfo);

        ServiceInstance instance = loadBalance.choose(jobInfo.getJobGroup());

        // Job执行参数
        String serialNo = Ft.DateTime.formatLocalDateTime(LocalDateTime.now(), DateTimeUtil.DATE_TIME_FORMAT_1);
        LocalDate jobDate = LocalDate.now();
        Map<String, Object> jobParam = new HashMap<>();

        Object o = Ft.LocalCache.get(jobInfo.getJobGroup() + ":" + jobInfo.getJobName() + ":" + "jobParam");
        if (o != null) {
            Map<String, Object> map = Ft.JSON.parseToMap((String) o);
            Object date = map.get("jobDate");
            if (date != null) {
                jobDate = Ft.DateTime.parseLocalDate((String) date, "yyyy-MM-dd");
            }

            jobParam.putAll(map);
        }

        jobParam.put("serialNo", serialNo);
        jobParam.put("jobType", jobInfo.getJobType());
        jobParam.put("jobDate", jobDate);

        TaskJobLogBo log = setLog(jobInfo, instance.getHost(), serialNo);
        // 批量日切 TODO
        log.setJobDate(jobDate);
        taskJobLogService.createObj(log);

        Map<String, Object> body = setRequestBody(jobInfo, log);
        body.put("jobParam", Ft.JSON.serializer(jobParam));

        // 构建服务实例的 URL
        String url = instance.getUri().toString() + "/job/run";
        // 发起 HTTP 请求
        call(url, log, body);
    }

    private Map<String, Object> setRequestBody(TaskJobInfoBo jobInfo, TaskJobLogBo log) {
        Map<String, Object> body = new HashMap<>();
        body.put("logId", log.getId());
        body.put("serviceName", jobInfo.getServiceName());
        body.put("jobName", jobInfo.getJobName());
        body.put("jobGroup", jobInfo.getJobGroup());
        body.put("jobType", jobInfo.getJobType());
        body.put("jobDesc", jobInfo.getJobDesc());
        body.put("remark", jobInfo.getRemark());
        body.put("tenantId", jobInfo.getTenantId());
        return body;
    }

    private TaskJobLogBo setLog(TaskJobInfoBo jobInfo, String host, String serialNo) {
        TaskJobLogBo log = new TaskJobLogBo();
        log.setJobName(jobInfo.getJobName());
        log.setJobType(jobInfo.getJobType());
        log.setJobGroup(jobInfo.getJobGroup());
        log.setJobDesc(jobInfo.getJobDesc());
        log.setJobParam(jobInfo.getJobParam());
        log.setSerialNo(serialNo);
        log.setServiceName(jobInfo.getServiceName());
        log.setJobHost(host);
        log.setJobStatus(JobExecuteStatusEnum.RUNNING.getCode());
        log.setStartTime(LocalDateTime.now());
        log.setTenantId(jobInfo.getTenantId());

        return log;
    }

    /**
     * 手动补偿处理
     *
     * @param jobInfo
     * @param serialNo
     * @param param
     * @param host
     */
    public void resolveJob(TaskJobInfoBo jobInfo, String serialNo, String param, String host) {
        // Job执行参数
        Map<String, Object> jobParam = new HashMap<>();
        if (Ft.String.isBlank(serialNo)) {
            serialNo = Ft.DateTime.formatLocalDateTime(LocalDateTime.now(), DateTimeUtil.DATE_TIME_FORMAT_1);
        }
        jobParam.put("serialNo", serialNo);
        jobParam.put("jobType", jobInfo.getJobType());
        jobParam.put("jobDate", LocalDate.now());

        TaskJobLogBo log = setLog(jobInfo, host, serialNo);
        log.setJobDate(LocalDate.now());
        if (param != null) {
            Map<String, Object> map = Ft.JSON.parseToMap(param);
            log.setJobDate(Ft.DateTime.parseLocalDate((String) map.get("jobDate"), "yyyy-MM-dd"));
            jobParam.putAll(map);
        }
        taskJobLogService.createObj(log);

        Map<String, Object> body = setRequestBody(jobInfo, log);
        body.put("jobParam", Ft.JSON.serializer(jobParam));

        // 构建服务实例的 URL
        String url = host + "/job/resolve";
        // 发起 HTTP 请求
        call(url, log, body);
    }

    private void call(String url, TaskJobLogBo log, Map<String, Object> body) {
        HttpHeaders headers = new HttpHeaders();
        headers.add(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE);
        HttpEntity<Map<String, Object>> entity = new HttpEntity<>(body, headers);
        try {
            ResponseEntity<Rt> responseEntity = restTemplate.postForEntity(url, entity, Rt.class);
            Rt<?> rt = responseEntity.getBody();
            logger.info("调度任务返回结果：{}", Ft.JSON.serializer(rt));
        } catch (ResourceAccessException e) {
            // 捕获超时或连接异常
            Throwable cause = e.getRootCause();
            if (cause instanceof java.net.SocketTimeoutException || cause instanceof java.net.ConnectException) {
                logger.error("调度任务失败，连接超时或服务器不可达：--->", e);

                // 保存异常日志
                log.setJobStatus(JobExecuteStatusEnum.FAIL.getCode());
                log.setJobMsg("调度任务失败，连接超时或服务器不可达!");
                log.setJobTrace(ExceptionUtils.getStackTrace(e));
                taskJobLogService.updateObj(log);
            }
        }
    }
}
